package com.aidansmeaton.weightmanager;

import java.util.Calendar;

import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TabActivity;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.DatePicker;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import android.widget.Toast;

import com.aidansmeaton.weightmanager.data.DatabaseHelper;
import com.aidansmeaton.weightmanager.util.TimeFormatter;

/** The first activity in the app. */
public class DiaryActivity extends TabActivity implements OnClickListener {
	
	private static final int DATE_DIALOG_ID = 0;
    private static final int MILLIS_IN_A_DAY = 86400000;
    private static final double SHAKE_DETECTION_LIMIT = 2;
	
	private TabHost tabHost;
    private Calendar c;
    
    private TextView datePicker;
    private ImageView leftArrow;
    private ImageView rightArrow;
    private RelativeLayout header;
    
    private SensorManager mSensorManager;
    private float mAccel; 			// acceleration apart from gravity
    private float mAccelCurrent; 	// current acceleration including gravity
    private float mAccelLast; 		// last acceleration including gravity
    private SensorEventListener mSensorListener;
    
    private static final int SWIPE_MIN_DISTANCE = 120;
    private static final int SWIPE_MAX_OFF_PATH = 250;
    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
    private GestureDetector gestureDetector;
    View.OnTouchListener gestureListener;
	
	/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.diary);
        
        DatabaseHelper db = DatabaseHelper.getInstance(this);
        
        // Set up tabs
	    setupTabHost();
	    //startingTab = 0;
	    
	    // Set up calendar and date picker
	    datePicker = (TextView) findViewById(R.id.diary_date);
	    leftArrow = (ImageView) findViewById(R.id.left_arrow);
	    rightArrow = (ImageView) findViewById(R.id.right_arrow);
	    header = (RelativeLayout) findViewById(R.id.header);
        c = Calendar.getInstance(); // current date      
        
        // Show tabs and date
        updateDisplay();
        
        // Sensor stuff for managing "shakes"
        try {
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
        mAccel = 0.00f;
        mAccelCurrent = SensorManager.GRAVITY_EARTH;
        mAccelLast = SensorManager.GRAVITY_EARTH;
        mSensorListener = createSensorListener();
        } catch (Exception e) {}
        
        // Gesture detection
        gestureDetector = new GestureDetector(new MyGestureDetector());
        gestureListener = new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                return gestureDetector.onTouchEvent(event);
            }
        };
	    datePicker.setOnClickListener(DiaryActivity.this);
	    datePicker.setOnTouchListener(gestureListener);

        // Stop soft keyboard popping up automatically
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
	    
	}
    
    public void resume(){
    	onResume();
    }
    
    @Override
    protected void onResume() {
    	super.onResume();
    	try {
    		mSensorManager.registerListener(mSensorListener, mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
    	} catch (Exception e) {}
    }

    @Override
    protected void onStop() {
    	super.onStop();
    	saveDefaultTab();
    	try {
    	mSensorManager.unregisterListener(mSensorListener);
    	} catch (Exception e) {}
    }
    
    public void onClick(View v) {
    	// Find out if it was a regular click or a "Swipe"
    	String tag = (String) v.getTag();
    	
    	// If it has no tag, it's just a regular click
    	if (tag == null) showDialog(DATE_DIALOG_ID);
    }
    
    public void leftClick(View v){
    	changeDay(false,1);
    }
    
    public void rightClick(View v){
    	changeDay(true,1);
    }
    
    public void changeDay(boolean forward, int numberOfDays){
    		
    	// Increment/decrement days (in terms of milliseconds)
    	c.add(Calendar.MILLISECOND, (forward ? 1 : -1) * numberOfDays * MILLIS_IN_A_DAY);
    	datePicker.setAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
    	validateDate();
        
        // Update the display
    	saveDefaultTab();
        updateDisplay();
    }
    
    public void resetToToday(){
    	c = Calendar.getInstance();
    	datePicker.setAnimation(AnimationUtils.loadAnimation(this, android.R.anim.fade_in));
    	saveDefaultTab();
        updateDisplay();
    }
    
    /**
     * Ensures the date is not in the future
     */
    private void validateDate(){    	
    	// Can't go any higher than current date/time
    	if (c.getTimeInMillis() > System.currentTimeMillis()) resetToToday();
    }
    
    protected SensorEventListener createSensorListener() {
    	return new SensorEventListener() {
	        public void onSensorChanged(SensorEvent se) {
	            float x = se.values[0];
	            float y = se.values[1];
	            float z = se.values[2];
	            mAccelLast = mAccelCurrent;
	            mAccelCurrent = (float) Math.sqrt((double) (x*x + y*y + z*z));
	            float delta = mAccelCurrent - mAccelLast;
	            mAccel = mAccel * 0.9f + delta; // perform low-cut filter
	            
	            // if a shake is detected
	            if (mAccel > SHAKE_DETECTION_LIMIT) {
	            	DiaryActivity.this.saveDefaultTab();
	          	  	resetToToday();
	            }
	        }
          	public void onAccuracyChanged(Sensor sensor, int accuracy) {
          	}
        };
    }
    
    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
        case DATE_DIALOG_ID:
            return new DatePickerDialog(this, mDateSetListener,
                        c.get(Calendar.YEAR),
                        c.get(Calendar.MONTH),
                        c.get(Calendar.DAY_OF_MONTH));
        }
        return null;
    }
    
    // the callback received when the user "sets" the date in the dialog
    private DatePickerDialog.OnDateSetListener mDateSetListener =
            new DatePickerDialog.OnDateSetListener() {
                public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
                	c.set(Calendar.YEAR, year);
                	c.set(Calendar.MONTH, monthOfYear);
                	c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
                	validateDate();
                    updateDisplay();
                }
            };
            
    private int defaultTab(){
    	return getSharedPreferences("Diary", 0).getInt("Tab", 0);
    }
    
    private void saveDefaultTab(){
    	getSharedPreferences("Diary", 0).edit().putInt("Tab", tabHost.getCurrentTab()).commit();
    }     
            
    // updates the date we display in the TextView
    private void updateDisplay() {
    	
    	// if it's today
    	if (c.getTimeInMillis() >= ((System.currentTimeMillis()/MILLIS_IN_A_DAY)*MILLIS_IN_A_DAY)){
    		rightArrow.setVisibility(View.INVISIBLE);
    		header.setBackgroundDrawable(getResources().getDrawable(R.drawable.gradient_teal));
    		Toast.makeText(DiaryActivity.this, getString(R.string.diary_shake_detection), Toast.LENGTH_SHORT).show();
    	}
    	else {
    		rightArrow.setVisibility(View.VISIBLE);
    		header.setBackgroundDrawable(getResources().getDrawable(R.drawable.gradient_teal_dark));
    	}
    	
        datePicker.setText(
            new StringBuilder()
            		.append(TimeFormatter.getDayAsString(c.get(Calendar.DAY_OF_WEEK))).append(" ")
                    .append(c.get(Calendar.DAY_OF_MONTH)).append(" ")
                    .append(TimeFormatter.getMonthAsString(c.get(Calendar.MONTH))).append(" ")
                    .append(c.get(Calendar.YEAR)));
        
    	setupTabHost();
    	updateTabContents();
    }
    
    private void updateTabContents(){
    	
    	tabHost.setCurrentTab(0);
    	tabHost.clearAllTabs();
    	
    	String date = TimeFormatter.getDatabaseDateString(c);
    	
    	Intent intent = new Intent().setClass(this, DiaryWeightActivity.class);
	    intent.putExtra("Date", date);
	    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	    setupTab(getResources().getDrawable(R.drawable.ic_weight), "Weight", intent);
	    
	    intent = new Intent().setClass(this, DiaryFoodActivity.class);
	    intent.putExtra("Date", date);
	    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	    setupTab(getResources().getDrawable(R.drawable.ic_food), "Food", intent);
	    
	    intent = new Intent().setClass(this, DiaryExerciseActivity.class);
	    intent.putExtra("Date", date);
	    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	    setupTab(getResources().getDrawable(R.drawable.ic_exercise), "Exercise", intent);
	    
	    intent = new Intent().setClass(this, DiaryMoodActivity.class);
	    intent.putExtra("Date", date);
	    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	    setupTab(getResources().getDrawable(R.drawable.ic_mood), "Mood", intent);

	    intent = new Intent().setClass(this, DiaryPhotoActivity.class);
	    intent.putExtra("Date", date);
	    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
	    setupTab(getResources().getDrawable(R.drawable.ic_mirror), "Photo", intent);
	    
	    tabHost.setCurrentTab(defaultTab());
	    
    }
    
    /**
	 * @author Josh Clemm
	 * @see http://code.google.com/p/android-custom-tabs/
	 * @see http://joshclemm.com/blog/?p=136
	 */
	private void setupTab(final Drawable icon, String tag, Intent intent) {
		View tabview = createTabView(tabHost.getContext(), icon);
		TabSpec setContent = tabHost.newTabSpec(tag).setIndicator(tabview).setContent(intent);
		tabHost.addTab(setContent);
	}

	/**
	 * @author Josh Clemm
	 * @see http://code.google.com/p/android-custom-tabs/
	 * @see http://joshclemm.com/blog/?p=136
	 */
	private void setupTabHost() {
		tabHost = (TabHost) findViewById(android.R.id.tabhost);
		tabHost.setup();
	}
	
	/**
	 * @author Josh Clemm
	 * @see http://code.google.com/p/android-custom-tabs/
	 * @see http://joshclemm.com/blog/?p=136
	 */
	private static View createTabView(final Context context, final Drawable icon) {
		View view = LayoutInflater.from(context).inflate(R.layout.tabs_bg, null);
		
		ImageView iv = (ImageView) view.findViewById(R.id.tabIcon);
		iv.setImageDrawable(icon);
		
		return view;
	}
	
	/**
	 * @author Code Shogun
	 * @see http://www.codeshogun.com/blog/2009/04/16/how-to-implement-swipe-action-in-android/
	 */
	private class MyGestureDetector extends SimpleOnGestureListener {
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            try {
                if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
                    return false;
                // Right to left swipe - Go back a day
                if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
            		changeDay(false,1);
                }
                // Left to right swipe - Go forward a day
                else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) {
                    changeDay(true,1);
                }
            } catch (Exception e) {
                // nothing
            }
            return false;
        }

    }
	
}